从源码角度分析Spring batch里ItemReader的使用

想要成为技术大牛,必需要看源码,从源码分析。下面分析几种springBatch里的ItemReader子类。

ItemReader<T>(泛型)是所有Reader的父类。且只有一个方法Tread() throws Exception, UnexpectedInputException, ParseException,NonTransientResourceException;

所以子类的参数或通过spring注入或创建默认。

所有子类如下图:


挑几个常用的做介绍。

文件读取类

FlatFileItemReader:文件读取类,属性及方法如下:


原来该类通过BufferedReader一行一行读取文件数据,同时以#开头的视做注释,跳过读取。真正做事的是doRead方法。而doRead():

protected T doRead() throws Exception {
		if (noInput) {
			return null;
		}

		String line = readLine();

		if (line == null) {
			return null;
		}
		else {
			try {
				return lineMapper.mapLine(line, lineCount);
			}
			catch (Exception ex) {
				throw new FlatFileParseException("Parsing error at line: " + lineCount + " in resource=["
						+ resource.getDescription() + "], input=[" + line + "]", ex, line, lineCount);
			}
		}
	}

在读取之前会有打开的操作doOpen():

protected void doOpen() throws Exception {
		Assert.notNull(resource, "Input resource must be set");
		Assert.notNull(recordSeparatorPolicy, "RecordSeparatorPolicy must be set");

		noInput = true;
		if (!resource.exists()) {
			if (strict) {
				throw new IllegalStateException("Input resource must exist (reader is in 'strict' mode): " + resource);
			}
			logger.warn("Input resource does not exist " + resource.getDescription());
			return;
		}

		if (!resource.isReadable()) {
			if (strict) {
				throw new IllegalStateException("Input resource must be readable (reader is in 'strict' mode): "
						+ resource);
			}
			logger.warn("Input resource is not readable " + resource.getDescription());
			return;
		}

		reader = bufferedReaderFactory.create(resource, encoding);
		for (int i = 0; i < linesToSkip; i++) {
			String line = readLine();
			if (skippedLinesCallback != null) {
				skippedLinesCallback.handleLine(line);
			}
		}
		noInput = false;
	}

见红色字体,必需保证资源存在可读,同时初始化一个BufferedReader实例。

 

 

配置例子:

假设数据是

那reader可如下配置:


<bean id="playerFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
		<property name="resource" value="classpath:data/footballjob/input/${player.file.name}" />
		<property name="lineMapper">
			<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
				<property name="lineTokenizer">
					<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
						<property name="names" value="ID,lastName,firstName,position,birthYear,debutYear" />
					</bean>
				</property>
				<property name="fieldSetMapper">
					<bean class="org.springframework.batch.sample.domain.football.internal.PlayerFieldSetMapper" />
				</property>
			</bean>
		</property>
	</bean>

这样数据和红色字段指定的names字段就会自动映射。

故,想要使用FlatFileItemReader必需配resourcelineMapper两个属性,其它可选配。指定names字段,这样,字段映射成一个对象传给ItemWriter<T>

 

数据库操作:

由于持久层有hiberate,ibatis,mybatis,所以不同的框架读取不一样:

 

游标读取方式:JdbcCursorItemReader。通过查看JdbcCursorItemReader


及其父类AbstractCursorItemReader:


的代码,我们发现必要参数是sql, dataSource及rowMapper必需注入。

AbstractCursorItemReader.doRead():

protected T doRead() throws Exception {
		if (rs == null) {
			throw new ReaderNotOpenException("Reader must be open before it can be read.");
		}
	
		try {
			if (!rs.next()) {
				return null;
			}
			int currentRow = getCurrentItemCount();
			T item = readCursor(rs, currentRow);
			verifyCursorPosition(currentRow);
			return item;
		}
		catch (SQLException se) {
			throw getExceptionTranslator().translate("Attempt to process next row failed", getSql(), se);
		}
	}

不难发现rs是执行sql语句返回的结果集,该方法返回当前游标的的item,注意类型是T即泛型。所以,dataSource指定数据源,sql即指定查询语句,rowMapper对当前游标位置的数据进行包装,得到的结果即是指定的结构对象,我一直强调泛型,因为我们用返回的结果即是泛型指定的类型。

配置例子:

<bean id="customerSqlItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader">
		<property name="dataSource" ref="dataSource" />
		<property name="sql" value="SELECT id, name, credit FROM CUSTOMER " />
		<property name="rowMapper">
			<bean class="org.springframework.batch.sample.domain.trade.internal.CustomerCreditRowMapper" />
		</property>
	</bean>
CustomerCreditRowMapper.java:

public class CustomerCreditRowMapper implements RowMapper<CustomerCredit> {
	
	public static final String ID_COLUMN = "id";
	public static final String NAME_COLUMN = "name";
	public static final String CREDIT_COLUMN = "credit";

	@Override
	public CustomerCredit mapRow(ResultSet rs, int rowNum) throws SQLException {
        CustomerCredit customerCredit = new CustomerCredit();

        customerCredit.setId(rs.getInt(ID_COLUMN));
        customerCredit.setName(rs.getString(NAME_COLUMN));
        customerCredit.setCredit(rs.getBigDecimal(CREDIT_COLUMN));

        return customerCredit;
	}

Ibatis读取方式:

先看IbatisPagingItemReader的父类AbstractPagingItemReader类结构图:

分别可设置页号page,页尺寸pageSize默认10等,重点看看doRead()方法:

@Override
	protected T doRead() throws Exception {

		synchronized (lock) {

			if (results == null || current >= pageSize) {

				if (logger.isDebugEnabled()) {
					logger.debug("Reading page " + getPage());
				}

				doReadPage();
				page++;
				if (current >= pageSize) {
					current = 0;
				}

			}

			int next = current++;
			//返回下一个item
			if (next < results.size()) {
				return results.get(next);
			}
			else {
				return null;
			}

		}

	}

具体读操作由子类doReadPage方法完成,故现在看看IbatisPagingItemReader



配queryId(映射文件xml里的方法名称),sqlMapCLient实例即是ibatis的配置文件,还要配dataSource.看看它是怎么完成查询的

@Override
	@SuppressWarnings("unchecked")
	protected void doReadPage() {
		Map<String, Object> parameters = new HashMap<String, Object>();
		if (parameterValues != null) {
			parameters.putAll(parameterValues);
		}
		parameters.put("_page", getPage());
		parameters.put("_pagesize", getPageSize());
		parameters.put("_skiprows", getPage() * getPageSize());
		if (results == null) {
			results = new CopyOnWriteArrayList<T>();
		}
		else {
			results.clear();
		}
		results.addAll(sqlMapClientTemplate.queryForList(queryId, parameters));//过程以目了然
	}

所以IbatisPagingItemReader必需配的参数为queryId,sqlMapCLient, dataSource,其它选配。

配置例子:

<bean id="itemReader"
		class="org.springframework.batch.item.database.IbatisPagingItemReader">
		<property name="queryId" value="getAllCustomerCredits" />
		<property name="sqlMapClient" ref="sqlMapClient" />
		<property name="dataSource" ref="dataSource"/>
	</bean>
<bean id="sqlMapClient" class="com.ibatis.sqlmap.client.SqlMapClientBuilder" factory-method="buildSqlMapClient">
		<constructor-arg value="ibatis-config.xml"/>
	</bean>

ibatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
	"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
  <sqlMap resource="ibatis-customer-credit.xml"/>
</sqlMapConfig>

ibatis-customer-credit.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
	"http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Customer">
  <resultMap id="result" class="org.springframework.batch.sample.domain.trade.CustomerCredit">
    <result property="name" column="NAME" />
    <result property="credit" column="CREDIT" />
  </resultMap>

  <select id="getAllCustomerCreditIds" resultClass="int">
    select ID from CUSTOMER
  </select>
  
  <select id="getAllCustomerCredits" resultMap="result">
 	select ID, NAME, CREDIT from CUSTOMER 
  </select>
  
  <select id="getCustomerCreditById" parameterClass="int" resultMap="result">
  	select NAME, CREDIT from CUSTOMER where ID = #value#
  </select>
  
  <update id="updateCredit" parameterClass="org.springframework.batch.sample.domain.trade.CustomerCredit" >
  	update CUSTOMER set CREDIT = #credit# where NAME = #name#
  </update>
</sqlMap>

Ps:不需要对结果集包装,因为ibatis里将读取的数据映射。和spring-batch框架没关系

总结,以上只介绍了几咱读取的类,常用读取的要么文件要么数据库,会这两种类型即可,其实简单。




  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Batch是一个轻量级的、完善的批处理框架,旨在帮助企业建立健壮、高效的批处理应用。它提供了丰富的功能和工具,可用于处理大量的数据和复杂的业务逻辑。Spring Boot对Spring Batch提供了良好的支持,其码位于org.springframework.boot.autoconfigure.batch包下。要使用Spring Batch,首先需要在配置文件中设置spring.batch.initializer.enabled为true,以初始化Spring Batch的数据库。在Spring Boot和Spring Batch配合使用的过程中,可以使用Spring Batch的各种特性,如批量读取、处理和写入数据、事务管理、任务调度等,以构建可靠且高效的批处理应用程序。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [SpringBatch 使用](https://blog.csdn.net/M_Jack/article/details/90482955)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [详解SpringBoot和SpringBatch 使用](https://download.csdn.net/download/weixin_38672800/12757077)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值